#include "glWidget.h"
#include <QKeyEvent>
#include <QDebug>
#include <cmath>

double PI(4*std::atan(1));

GLWidget::GLWidget(QWidget* parent, unsigned int fps)
  : QGLWidget(parent),
    mFramesPainted(0),
    mXRot(0),
    mYRot(0),
    mZRot(0)
{
  connect(&mTimer, SIGNAL(timeout()), this, SLOT(updateGL()));
  mTimer.start(1000/fps);
  mTime.start();
  setFocusPolicy(Qt::StrongFocus);

  qDebug() << "GLWidget started";
}

GLWidget::~GLWidget()
{
  mTimer.stop();
}

void GLWidget::initializeGL()
{
  loadGLTexture("testimage.png", mTextures);
  glEnable(GL_TEXTURE_2D); //enable 2D texture mapping

  glShadeModel(GL_SMOOTH); //smooth shading
  glClearColor(0, 0, 0, 0); //black background

  //depth buffer setup
  glClearDepth(1);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //best perspective correction

  glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //fill back face
  glPolygonMode(GL_FRONT, GL_LINE); //fill front face with lines

  for(unsigned int i(0); i < GRID_SIZE; ++i) //initialize grid
  {
    for (unsigned int j(0); j < GRID_SIZE; ++j)
    {
      mGrid[i][j][0] = i*1.0/5 - 4.5;
      mGrid[i][j][1] = j*1.0/5 - 4.5;
      mGrid[i][j][2] = 1*sin(i*1.0/5*40.0/360*PI*2);
    }
  }
}

void GLWidget::resizeGL(int w, int h)
{
  glViewport(0, 0, w, h); //reset viewport

  //setup perspective view
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45, w*1./h, 0.1, 100.);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

void GLWidget::paintGL()
{
  static int time1(0), time2(90);
  for(unsigned int i(0); i < GRID_SIZE; ++i) //initialize grid
  {
    for (unsigned int j(0); j < GRID_SIZE; ++j)
    {
      mGrid[i][j][0] = i*1.0/5 - 4.5;
      mGrid[i][j][1] = j*1.0/5 - 4.5;
      mGrid[i][j][2] = 0.5*sin((i*1.0/5*40.0+time1)/360*PI*2)+0.75*sin((j*1.0/5*40.0+time2-20)/360*PI*2);
    }
  }
  time1 = (time1+40)%360;
  time2 = (time2+25)%360;
  mXRot += 0.3f;
  mYRot += 0.2f;
  mZRot += 0.4f;

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //clear screen and depth buffer
  glLoadIdentity(); //reset view

  glTranslatef(0, 0, -12);

  glRotatef(mXRot, 1, 0, 0);
  glRotatef(mYRot, 0, 1, 0);
  glRotatef(mZRot, 0, 0, 1);

  glBindTexture(GL_TEXTURE_2D, mTextures[0]);
  glBegin(GL_QUADS);
  glColor3f(1, 1, 1);
  glLineWidth(2);
  for (unsigned int i(0); i < GRID_SIZE-1; ++i)
  {
    for (unsigned int j(0); j < GRID_SIZE-1; ++j)
    {
      double fi(i*1.0/(GRID_SIZE-1));
      double fib((i+1.0)/(GRID_SIZE-1));
      double fj(j*1.0/(GRID_SIZE-1));
      double fjb((j+1.0)/(GRID_SIZE-1));

      glTexCoord2f(fi, fj); //bottom left
      glVertex3f(mGrid[i][j][0], mGrid[i][j][1], mGrid[i][j][2]);

      glTexCoord2f(fi, fjb); //top left
      glVertex3f(mGrid[i][j+1][0], mGrid[i][j+1][1], mGrid[i][j+1][2]);

      glTexCoord2f(fib, fjb ); //top right
      glVertex3f(mGrid[i+1][j+1][0], mGrid[i+1][j+1][1], mGrid[i+1][j+1][2]);

      glTexCoord2f(fib, fj); //bottom right
      glVertex3f(mGrid[i+1][j][0], mGrid[i+1][j][1], mGrid[i+1][j][2]);
    }
  }
  glEnd();

  //calculate fps
  ++mFramesPainted;
  if (mTime.elapsed() > 1000)
  {
    emit fps(mFramesPainted * 1000 / mTime.restart());
    mFramesPainted = 0;
  }
}

void GLWidget::keyPressEvent(QKeyEvent *e)
{
  switch (e->key())
  {
  default:
    QGLWidget::keyPressEvent(e);
  }
}

void GLWidget::loadGLTexture(const QString &filename, GLuint textures[3])
{
  //load image
  QImage temp(filename);
  if (temp.isNull())
  {
    temp = QImage(16, 16, QImage::Format_RGB32);
    temp.fill(Qt::green);
  }
  QImage glformatted(QGLWidget::convertToGLFormat(temp)); //convert to gl format

  glGenTextures(1, &textures[0]); //create 1 texture

  glBindTexture(GL_TEXTURE_2D, textures[0]); //create 2d texture at ret
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //filter when image is smaller than texture: good filter
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //filter when image is larger than texture: good filter
  glTexImage2D(GL_TEXTURE_2D, //create 2d texture
               0, //level of detail
               3, //number of data components (rgb = 3)
               glformatted.width(), glformatted.height(), //size of texture
               0, //border
               GL_RGBA, //use rgb in this order
               GL_UNSIGNED_BYTE, //image data is unsigned bytes
               glformatted.bits()); //pointer to image data
}
